import dataclasses
import itertools
import sys
import os
from typing import List, Tuple

import joblib
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly import subplots

_ = os.path.abspath(os.path.dirname(__file__))  # 返回当前文件路径
root_path = os.path.abspath(os.path.join(_, '..'))  # 返回根目录文件夹

sys.path.append(root_path)

from alphalib.backtest.playback import timer
from alphalib.contrib.strategy import base_strategy, z1_conf_types, z1_playback, z1_strategy
from Config import *

@dataclasses.dataclass
class Z1StrategyTraverseConfig:
    factor_list: List[Tuple[str, str]] = dataclasses.field(default_factory=list)
    bh_list: List[int] = dataclasses.field(default_factory=list)
    date_range_list: List[Tuple[str, str]] = dataclasses.field(default_factory=list)
    filter_before_params_list: List[List[z1_conf_types.F1FilterParams]] = dataclasses.field(default_factory=list)
    base_stg_conf: z1_strategy.Z1StrategyConfig = dataclasses.field(default_factory=z1_strategy.Z1StrategyConfig)
    base_playback_conf: z1_playback.Z1PlaybackConfig = dataclasses.field(default_factory=z1_playback.Z1PlaybackConfig)
    tmp_pkl_path: str = os.path.join(z1_playback.ROOT_PATH, 'data', 'tmp')
    traverse_path: str = os.path.join(z1_playback.ROOT_PATH, 'data', 'output', 'traverse')
    n_jobs: int = n_jobs #min(max(os.cpu_count() - 2, 1),59)
    factor_tf: bool = False

def gen_z1_stg_conf(
        factor_name: (str, str), bh: int, filter_before_params: List[z1_conf_types.F1FilterParams],
        trav_conf: Z1StrategyTraverseConfig
):
    factor_long, factor_short = factor_name

    stg_conf = dataclasses.replace(
        trav_conf.base_stg_conf,
        strategy_name=f'{factor_long}_bh{bh}',
        long_factors=[z1_conf_types.F1FactorConfig(factor_long, trav_conf.factor_tf, bh, 0, 1)],
        short_factors=[z1_conf_types.F1FactorConfig(factor_short, trav_conf.factor_tf, bh, 0, 1)],
        filter_before_params=filter_before_params,
    )

    return z1_strategy.Z1Strategy(stg_conf), f'{factor_long}_{factor_short}_bh{bh}'


def z1_run_playback(df_filename: str, stg: base_strategy.BaseStrategy, playback_conf) -> np.float64:
    res, *_ = z1_playback.run_playback(stg, playback_conf, all_spot_df=df_filename, all_swap_df=df_filename)
    return res['累积净值'].iloc[0]


def plolty_plot(df: pd.DataFrame, save_dir: str, name: str):
    rows = len(df.columns)
    fig = subplots.make_subplots(rows=rows, cols=1, shared_xaxes=True, shared_yaxes=True, vertical_spacing=0.05)

    for i, col in enumerate(df.columns):
        trace = go.Bar(x=df.index, y=df[col], name=f"{col}")
        fig.add_trace(trace, i + 1, 1)

    fig.update_layout(height=200 * rows, showlegend=True, title_text=name)
    fig.write_html(
        os.path.join(save_dir, f"{name}.html")
    )
    fig.show()


def traverse_z1_strategy(trav_conf: Z1StrategyTraverseConfig):
    all_spot_df, all_swap_df = z1_playback.read_all_df_for_backtest(trav_conf.base_playback_conf)
    all_spot_df, all_swap_df = z1_playback.trans_spot_to_swap(all_spot_df, all_swap_df)
    all_df = dict(all_spot_df, **all_swap_df)
    all_df_cache_fn = os.path.join(trav_conf.tmp_pkl_path, "all_df_cache.pkl")
    if not os.path.exists(trav_conf.tmp_pkl_path):
        os.makedirs(trav_conf.tmp_pkl_path)
    joblib.dump(all_df, all_df_cache_fn)
    for factor_name, filter_before_params in itertools.product(
            trav_conf.factor_list,
            trav_conf.filter_before_params_list
    ):
        fl_name_suffix = '_'.join([
            f.to_col_name()
            for f in set(x.to_filter_config() for x in filter_before_params)
        ])
        fl_name = f"遍历净值_{fl_name_suffix}"
        res_dict = {}
        for start_date, end_date in trav_conf.date_range_list:
            col_name = f'{start_date}-{end_date}'
            param_list = []
            for bh in trav_conf.bh_list:
                stg, module_name = gen_z1_stg_conf(factor_name, bh, filter_before_params, trav_conf)
                play_conf = dataclasses.replace(
                    trav_conf.base_playback_conf,
                    compound_name=module_name,
                    start_date=start_date,
                    end_date=end_date,
                )
                param_list.append((all_df_cache_fn, stg, play_conf))
            res_list = z1_playback.parallel_process(z1_run_playback, param_list, n_jobs=trav_conf.n_jobs)
            res_dict[col_name] = pd.Series(res_list, index=trav_conf.bh_list)
        res_df = pd.DataFrame(res_dict)
        file_name = f"traverse_{factor_name[0]}_{factor_name[1]}"
        save_dir = os.path.join(trav_conf.traverse_path, file_name)
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        res_df.to_csv(os.path.join(save_dir, f"{fl_name}.csv"))
        # 绘制柱状图
        plolty_plot(res_df, save_dir, fl_name)


if __name__ == '__main__':
    with timer.timer('参数遍历'):
        base_stg_conf = z1_strategy.Z1StrategyConfig(
            hold_period=1,
            if_use_spot=False,
            long_weight=1, short_weight=1,
            long_coin_num=5, short_coin_num=5,
            short_black_list = ['BTCUSDT.swap', 'ETHUSDT.swap', 'USDCUSDT.swap']
        )
        trav_conf = Z1StrategyTraverseConfig(
            factor_list=[
                ('BollCountPunish', 'BollCountPunish'),
            ],
            factor_tf = True,
            #bh_list=[24,35,50,60,77,90,110,120,130,140,153,168,180,200,220,240,270,320,360,420,480,540],

            #bh_list=[15,17,19,21,23,25, 33, 42, 51, 60, 69, 78, 87, 96, 105, 114, 123, 132, 141, 150, 159, 168, 177, 186, 195, 204, 213,
             #222, 231, 240, 249, 258, 267, 276, 285, 294, 303, 312, 321, 330, 339, 348, 357, 366, 375, 384, 393, 402,
             #411, 420, 429, 438, 447, 456, 465, 474, 483, 492,500],

            bh_list = [132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166,
                       168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204,
                       206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230],

            date_range_list=[
                 ('2021-01-01', '2021-12-31'), ('2022-01-01', '2022-12-31'),
                ('2023-01-01', '2023-12-31'), ('2024-01-01', '2024-06-30'),('2024-07-01', '2024-07-26'),
            ],
            filter_before_params_list=[[
                z1_conf_types.F1FilterParams('df1', '涨跌幅max_fl_24', 'value', 'lte', 0.2, False, False),
                z1_conf_types.F1FilterParams('df2', '涨跌幅max_fl_24', 'value', 'lte', 0.2, False, False),
                z1_conf_types.F1FilterParams('df1', 'QuoteVolumeSum_fl_24', 'rank', 'lte', 60, False, False),
                z1_conf_types.F1FilterParams('df2', 'QuoteVolumeSum_fl_24', 'rank', 'lte', 60, False, False),
            ]],
            base_stg_conf=base_stg_conf,
        )
        traverse_z1_strategy(trav_conf)
